1   /*
2    * Copyright (C) 2012 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.collect;
16  
17  import static com.google.common.base.Preconditions.checkNotNull;
18  import static com.google.common.base.Preconditions.checkPositionIndex;
19  
20  import com.google.common.annotations.GwtCompatible;
21  import com.google.common.base.Predicate;
22  
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Map.Entry;
29  import java.util.Set;
30  
31  import javax.annotation.Nullable;
32  
33  /**
34   * Implementation of {@link Multimaps#filterKeys(Multimap, Predicate)}.
35   *
36   * @author Louis Wasserman
37   */
38  @GwtCompatible
39  class FilteredKeyMultimap<K, V> extends AbstractMultimap<K, V> implements FilteredMultimap<K, V> {
40    final Multimap<K, V> unfiltered;
41    final Predicate<? super K> keyPredicate;
42  
43    FilteredKeyMultimap(Multimap<K, V> unfiltered, Predicate<? super K> keyPredicate) {
44      this.unfiltered = checkNotNull(unfiltered);
45      this.keyPredicate = checkNotNull(keyPredicate);
46    }
47  
48    @Override
49    public Multimap<K, V> unfiltered() {
50      return unfiltered;
51    }
52  
53    @Override
54    public Predicate<? super Entry<K, V>> entryPredicate() {
55      return Maps.keyPredicateOnEntries(keyPredicate);
56    }
57  
58    @Override
59    public int size() {
60      int size = 0;
61      for (Collection<V> collection : asMap().values()) {
62        size += collection.size();
63      }
64      return size;
65    }
66  
67    @Override
68    public boolean containsKey(@Nullable Object key) {
69      if (unfiltered.containsKey(key)) {
70        @SuppressWarnings("unchecked") // k is equal to a K, if not one itself
71        K k = (K) key;
72        return keyPredicate.apply(k);
73      }
74      return false;
75    }
76  
77    @Override
78    public Collection<V> removeAll(Object key) {
79      return containsKey(key) ? unfiltered.removeAll(key) : unmodifiableEmptyCollection();
80    }
81  
82    Collection<V> unmodifiableEmptyCollection() {
83      if (unfiltered instanceof SetMultimap) {
84        return ImmutableSet.of();
85      } else {
86        return ImmutableList.of();
87      }
88    }
89  
90    @Override
91    public void clear() {
92      keySet().clear();
93    }
94  
95    @Override
96    Set<K> createKeySet() {
97      return Sets.filter(unfiltered.keySet(), keyPredicate);
98    }
99  
100   @Override
101   public Collection<V> get(K key) {
102     if (keyPredicate.apply(key)) {
103       return unfiltered.get(key);
104     } else if (unfiltered instanceof SetMultimap) {
105       return new AddRejectingSet<K, V>(key);
106     } else {
107       return new AddRejectingList<K, V>(key);
108     }
109   }
110 
111   static class AddRejectingSet<K, V> extends ForwardingSet<V> {
112     final K key;
113 
114     AddRejectingSet(K key) {
115       this.key = key;
116     }
117 
118     @Override
119     public boolean add(V element) {
120       throw new IllegalArgumentException("Key does not satisfy predicate: " + key);
121     }
122 
123     @Override
124     public boolean addAll(Collection<? extends V> collection) {
125       checkNotNull(collection);
126       throw new IllegalArgumentException("Key does not satisfy predicate: " + key);
127     }
128 
129     @Override
130     protected Set<V> delegate() {
131       return Collections.emptySet();
132     }
133   }
134 
135   static class AddRejectingList<K, V> extends ForwardingList<V> {
136     final K key;
137 
138     AddRejectingList(K key) {
139       this.key = key;
140     }
141 
142     @Override
143     public boolean add(V v) {
144       add(0, v);
145       return true;
146     }
147 
148     @Override
149     public boolean addAll(Collection<? extends V> collection) {
150       addAll(0, collection);
151       return true;
152     }
153 
154     @Override
155     public void add(int index, V element) {
156       checkPositionIndex(index, 0);
157       throw new IllegalArgumentException("Key does not satisfy predicate: " + key);
158     }
159 
160     @Override
161     public boolean addAll(int index, Collection<? extends V> elements) {
162       checkNotNull(elements);
163       checkPositionIndex(index, 0);
164       throw new IllegalArgumentException("Key does not satisfy predicate: " + key);
165     }
166 
167     @Override
168     protected List<V> delegate() {
169       return Collections.emptyList();
170     }
171   }
172 
173   @Override
174   Iterator<Entry<K, V>> entryIterator() {
175     throw new AssertionError("should never be called");
176   }
177 
178   @Override
179   Collection<Entry<K, V>> createEntries() {
180     return new Entries();
181   }
182 
183   class Entries extends ForwardingCollection<Entry<K, V>> {
184     @Override
185     protected Collection<Entry<K, V>> delegate() {
186       return Collections2.filter(unfiltered.entries(), entryPredicate());
187     }
188 
189     @Override
190     @SuppressWarnings("unchecked")
191     public boolean remove(@Nullable Object o) {
192       if (o instanceof Entry) {
193         Entry<?, ?> entry = (Entry<?, ?>) o;
194         if (unfiltered.containsKey(entry.getKey())
195             // if this holds, then we know entry.getKey() is a K
196             && keyPredicate.apply((K) entry.getKey())) {
197           return unfiltered.remove(entry.getKey(), entry.getValue());
198         }
199       }
200       return false;
201     }
202   }
203   
204   @Override
205   Collection<V> createValues() {
206     return new FilteredMultimapValues<K, V>(this);
207   }
208 
209   @Override
210   Map<K, Collection<V>> createAsMap() {
211     return Maps.filterKeys(unfiltered.asMap(), keyPredicate);
212   }
213 
214   @Override
215   Multiset<K> createKeys() {
216     return Multisets.filter(unfiltered.keys(), keyPredicate);
217   }
218 }